import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import plotly.express as px
import seaborn as sns
# Lectura inicial de los datos
df_ago_sep = pd.read_excel("API_Ago_Sep.xlsx")
df4 = pd.read_csv('API_octubre.csv')
df_list = [df_ago_sep, df4]
# Concatenamos los DataFrames en uno solo
df_api = pd.concat(df_list, ignore_index=True)
# Dimensiones del dataset
print(df_api.shape)
# Vemos algunos ejemplos
df_api.sample(3)
(3658, 19)
| propertyCode | floor | price | propertyType | size | exterior | rooms | bathrooms | address | district | neighborhood | status | newDevelopment | hasLift | parkingSpace | priceByArea | latitude | longitude | description | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 3033 | 95179202 | bj | 317600.0 | flat | 75.0 | True | 2 | 2 | Calle Antonio Pérez | Chamartín | El Viso | good | False | False | NaN | 4235.0 | 4.044371e+01 | -3.682533e+00 | Piso en venta de 75m² distribuidos en 2 dormit... |
| 504 | 101357696 | 5 | 670000.0 | flat | 153.0 | True | 4 | 3 | Calle de Beatriz de Bobadilla | Moncloa | Ciudad Universitaria | good | False | True | True | 4379.0 | 4.044722e+06 | -3.716621e+07 | VENDOFÃCIL MONCLOA vende magnÃfico piso en z... |
| 2518 | 102308413 | 2 | 520000.0 | flat | 95.0 | True | 3 | 2 | Bronce | Arganzuela | Legazpi | good | False | True | NaN | 5474.0 | 4.038871e+01 | -3.690161e+00 | ¡EL MEJOR HOGAR PARA FORMAR UNA FAMILIA! Ponem... |
Vemos que son 3658 registros sobre los que se evalúan 19 características.
Creemos conveniente convertir alguna de las columnas en el índice del dataset, si es posible. A priori, la columna "propertyCode" parece que puede ser el índice del conjunto de datos, pues debe ser un código / identificador único.
Sin embargo, vemos que hay varios registros para un mismo código de propiedad (puede tratarse de un anuncio publicado por varias inmobiliarias a la vez). Por tanto, como la columna "propertyCode" no tiene entradas únicas (no corresponde con el total de registros del dataframe), no podemos definirlo como el índice del dataset.
# Para asegurarnos que es único, empezamos viendo la dimensión del conjunto de datos
print(f"Nº de filas del conjunto de datos: {df_api.shape[0]}")
# Vemos el número de valores únicos de la columna "propertyCode"
x = df_api["propertyCode"].nunique()
print(f"El número de valores únicos de propertyCode es: {x}")
Nº de filas del conjunto de datos: 3658 El número de valores únicos de propertyCode es: 2907
# Calculamos el número de registros duplicados
df_api["propertyCode"].duplicated().sum()
751
# Vemos cuales son los propertyCode de estos registros duplicados
df_api["propertyCode"].loc[df_api["propertyCode"].duplicated()==True]
481 99292196
482 100231885
483 102158208
540 100624353
557 101170115
...
3648 101675361
3653 101960793
3654 100169182
3656 95386118
3657 100440904
Name: propertyCode, Length: 751, dtype: int64
# Vemos que, por ejemplo, esta propiedad tiene dos entradas exactamente iguales, a excepción de las coordenadas geográficas
df_api.loc[df_api["propertyCode"] == 99292196]
| propertyCode | floor | price | propertyType | size | exterior | rooms | bathrooms | address | district | neighborhood | status | newDevelopment | hasLift | parkingSpace | priceByArea | latitude | longitude | description | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 166 | 99292196 | 4 | 490000.0 | flat | 119.0 | True | 3 | 2 | Calle de Sánchez Barcáiztegui | Retiro | PacÃfico | good | False | True | True | 4118.0 | 404069639.0 | -36727307.0 | ¡Aprovecha este descuento exclusivo! Disponib... |
| 481 | 99292196 | 4 | 490000.0 | flat | 119.0 | True | 3 | 2 | Calle de Sánchez Barcáiztegui | Retiro | PacÃfico | good | False | True | True | 4118.0 | 404065409.0 | -36722313.0 | ¡Aprovecha este descuento exclusivo! Disponib... |
Vemos que parece que hay 751 filas con el propertyCode duplicado. Si miramos algunos ejemplos, vemos que las únicas variaciones que encontramos son en las medidas de latitud y longitud, datos que son sensibles a pequeñas variaciones.
Intentaríamos conseguir más información sobre si es posible que un mismo inmueble pueda estar publicado por dos o más agencias diferentes, lo que también explicaría estas pequeñas variaciones en las coordenadas geográficas, según el dispositivo desde donde se extraigan.
También es posible que, debido a la forma en la que se han hecho las peticiones a la API (indicando un punto central y un barrio), se hayan solapado las circunferencias y se hayan devuelto varios registros por partida doble.
Bajo esta hipótesis, consideramos eliminar los registros con los propertyCode duplicados, quedándonos con cualquiera de los registros, suponiendo que ambas coordenadas son igualmente válidas.
# Por tanto, eliminamos los registros duplicados eliminar cuando se repitan los valores de la columna propertyCode
# Por defecto, se mantiene la primera aparición y se elimina el resto de filas repetidas.
df_api = df_api.drop_duplicates(subset = ['propertyCode'])
# Comprobamos cómo ha variado la dimensión del conjunto de datos
print(f"Nº de filas del conjunto de datos: {df_api.shape[0]}")
# Volvemos a comprobar el número de valores únicos de la columna "propertyCode"
x = df_api["propertyCode"].nunique()
print(f"El número de valores únicos de propertyCode es {x}")
Nº de filas del conjunto de datos: 2907 El número de valores únicos de propertyCode es 2907
Ahora ya hemos conseguido que el número de valores únicos de la columna "propertyCode" corresponda con el total de registros del dataframe, por lo que podemos definirlo como el índice del dataset.
# La variable "propertyCode" pasa a convertirse en el índice de la tabla
df_api = df_api.set_index(["propertyCode"])
##### Vemos el tipo de datos que contiene
df_api.dtypes
floor object price float64 propertyType object size float64 exterior object rooms int64 bathrooms int64 address object district object neighborhood object status object newDevelopment bool hasLift object parkingSpace object priceByArea float64 latitude float64 longitude float64 description object dtype: object
Indicamos qué recoge cada variable:
Infiriendo qué recoge cada uno de los datos que hemos descargado a partir de la web y de la observación del dataset, creemos que sería necesario implementar varias modificaciones para transformar las variables al tipo más adecuado. Sin embargo, no lo vamos a realizar ahora, sino que vamos a seguir con los siguientes puntos y lo tendremos en cuenta si es necesario transformarlo más adelante.
# La variable del código identificador de la propiedad está declarada como un entero, pero realmente es de tipo String, a pesar de que sean números. Puesto que no vamos a realizar operaciones aritméticas con este dato, no lo consideramos un entero, sino un literal, por lo que hacemos una nueva conversión para esta columna.
df_api["propertyCode"] = df_api["propertyCode"].astype("object")
# Hay variables booleanas que no están definidas como tal:
df_api["exterior"] = df_api["exterior"].astype("bool")
df_api["hasLift"] = df_api["hasLift"].astype("bool")
df_api["parkingSpace"] = df_api["parkingSpace"].astype("bool")
Si analizamos los NA's, vemos que 6 de las 19 variables que estamos teniendo en cuenta, tienen datos faltantes. En término porcentuales, apenas supone el 5% de los datos totales.
# Total de datos faltantes (en %)
datos_totales = np.product(df_api.shape) # 1) Total de datos en el dataset
valores_faltantes_totales = df_api.isnull().sum().sum() # 2) Total de datos nulos en el dataset
porcentaje_datos_faltantes = round((valores_faltantes_totales/datos_totales) * 100, 2)
print(f"Hay un {porcentaje_datos_faltantes}% de datos faltantes en el conjunto total de nuestro dataset")
Hay un 5.1% de datos faltantes en el conjunto total de nuestro dataset
# Obtenemos el número de datos faltantes por columna (ordenando de forma que el primer valor es la columna con más NAs)
df_api.isnull().sum().sort_values(ascending=False)
parkingSpace 1931 floor 321 exterior 240 hasLift 171 description 6 neighborhood 1 rooms 0 bathrooms 0 address 0 district 0 price 0 status 0 newDevelopment 0 size 0 propertyType 0 priceByArea 0 latitude 0 longitude 0 dtype: int64
A continuación, realizamos un estudio de estas seis variables para identificar la razón por la que hay datos faltantes e intentar encontrar la manera de rellenarlos. (hipótesis 3) Implementaremos algunas técnicas que pueden ayudarnos con los valores perdidos, pero tenemos en cuenta que probablemente también acabarán eliminando alguna información útil o añadiendo algo de ruido a nuestros datos.
CASO 1: Los valores faltantes corresponden a una categoría de la variable
U, suponiendo que se tratarían de viviendas unifamiliares, hipótesis que confirman los datos.df_api["floor"].value_counts(dropna = False)
1 419 2 336 bj 332 NaN 321 3 296 4 260 5 148 1 115 6 113 3 103 2 95 4 73 7 58 5 37 8 29 en 26 6 22 10 20 9 18 7 14 11 11 8 8 ss 8 11 7 9 6 14 6 13 5 12 3 st 3 15 3 -1 2 14 2 10 2 12 1 21 1 17 1 23 1 19 1 13 1 Name: floor, dtype: int64
# Observamos si los valores que son NA se tratan de chalets
df_api.loc[df_api["propertyType"]=="chalet"]
| floor | price | propertyType | size | exterior | rooms | bathrooms | address | district | neighborhood | status | newDevelopment | hasLift | parkingSpace | priceByArea | latitude | longitude | description | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| propertyCode | ||||||||||||||||||
| 102252223 | NaN | 1260000.0 | chalet | 307.0 | NaN | 5 | 4 | barrio Campo de las Naciones-Corralejos | Barajas | Campo de las Naciones-Corralejos | good | False | NaN | True | 4104.0 | 4.046840e+08 | -3.588403e+07 | GILMAR Consulting Inmobiliario Madrid, Conde O... |
| 101541620 | NaN | 1190000.0 | chalet | 240.0 | NaN | 4 | 3 | barrio Timón | Barajas | Timón | good | False | NaN | True | 4958.0 | 4.047147e+07 | -3.595860e+07 | QUARTIERS Expertos Inmobiliarios. Agents Immob... |
| 98857406 | NaN | 870000.0 | chalet | 293.0 | NaN | 6 | 4 | barrio Alameda de Osuna | Barajas | Alameda de Osuna | good | False | NaN | True | 2969.0 | 4.045738e+08 | -3.591681e+07 | Casa en venta muy cercana al Parque del Capric... |
| 101953745 | NaN | 275000.0 | chalet | 170.0 | NaN | 5 | 5 | Calle Trópico | Barajas | Casco Histórico de Barajas | renew | False | NaN | NaN | 1618.0 | 4.047533e+08 | -3.579087e+07 | VOhome propiedades Barajas vende casa en el ca... |
| 101854194 | NaN | 239000.0 | chalet | 81.0 | NaN | 4 | 1 | Calle de Gallur | Latina | Los Cármenes | good | False | NaN | NaN | 2951.0 | 4.039667e+08 | -3.738668e+07 | Fantástico chalet adosado a sólo 7 min de la... |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 102538200 | NaN | 899000.0 | chalet | 269.0 | NaN | 5 | 4 | barrio Salvador | San Blas | Salvador | good | False | NaN | True | 3342.0 | 4.043977e+01 | -3.633348e+00 | VOhome propiedades vende magnifico Chalet pare... |
| 101642974 | NaN | 899000.0 | chalet | 269.0 | NaN | 5 | 4 | barrio Salvador | San Blas | Salvador | good | False | NaN | True | 3342.0 | 4.043975e+01 | -3.633934e+00 | Precioso Chalet con amplio Jardin en Salvador ... |
| 102493593 | NaN | 1250000.0 | chalet | 341.0 | NaN | 5 | 5 | barrio Pinar del Rey | Hortaleza | Pinar del Rey | good | False | NaN | True | 3666.0 | 4.046994e+01 | -3.648141e+00 | Comercializa GILMAR CONSULTING INMOBILIARIO-AR... |
| 102635141 | NaN | 1250000.0 | chalet | 341.0 | NaN | 5 | 5 | barrio Pinar del Rey | Hortaleza | Pinar del Rey | good | False | NaN | True | 3666.0 | 4.047015e+01 | -3.648041e+00 | Magnifico chalet independiente en Pinar del Re... |
| 98788092 | NaN | 943000.0 | chalet | 425.0 | NaN | 3 | 4 | Calle de Estíbaliz | Hortaleza | Pinar del Rey | good | False | NaN | True | 2219.0 | 4.047044e+01 | -3.650761e+00 | Este casa / chalet se encuentra en Calle Estíb... |
157 rows × 18 columns
# Solución: podríamos convertir los valores nulos en la categoría "U" (unifamiliar)
df_api["floor"].fillna("U", inplace=True)
df_api["parkingSpace"].value_counts(dropna = False)
NaN 1931 True 976 Name: parkingSpace, dtype: int64
# Observamos si los valores que son NA se tratan de chalets
df_api.loc[df_api["parkingSpace"].isna()].head()
| floor | price | propertyType | size | exterior | rooms | bathrooms | address | district | neighborhood | status | newDevelopment | hasLift | parkingSpace | priceByArea | latitude | longitude | description | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| propertyCode | ||||||||||||||||||
| 100546624 | 3 | 2300000.0 | flat | 161.0 | True | 3 | 2 | Calle de Villanueva s/n | Barrio de Salamanca | Recoletos | good | False | True | NaN | 14286.0 | 404224303.0 | -36862786.0 | DIZA Consultores les presenta vivienda en edif... |
| 102200705 | 2 | 319000.0 | flat | 45.0 | True | 1 | 1 | Calle de la Villa | Centro | Palacio | good | False | True | NaN | 7089.0 | 404153319.0 | -37124811.0 | Gilmar consulting inmobiliario presenta para s... |
| 100688663 | 1 | 1300000.0 | flat | 258.0 | True | 4 | 2 | Calle HUERTAS | Centro | Huertas-Cortes | good | False | True | NaN | 5039.0 | 404149124.0 | -37014127.0 | Gilmar Consulting inmobiliario vende magnifica... |
| 100762638 | 1 | 660000.0 | flat | 183.0 | True | 2 | 2 | Calle estudios | Centro | Palacio | good | False | True | NaN | 3607.0 | 404177238.0 | -37101593.0 | Consulting inmobiliario Gilmar, presenta un he... |
| 100367054 | 4 | 695000.0 | flat | 135.0 | True | 3 | 2 | Calle de Augusto Figueroa | Centro | Chueca-Justicia | good | False | True | NaN | 5148.0 | 404239288.0 | -3698415.0 | Consulting inmobiliario Gilmar, presenta para ... |
# Solución: podríamos convertir los valores nulos en la categoría False
df_api["parkingSpace"].fillna(False, inplace=True)
CASO 2: Los valores faltantes corresponden a datos no registrados por error
df_api["exterior"].value_counts(dropna = False)
True 2389 False 278 NaN 240 Name: exterior, dtype: int64
# Observamos si los valores que son NA se tratan de chalets
df_api.loc[df_api["exterior"].isna()][-5:-1]
| floor | price | propertyType | size | exterior | rooms | bathrooms | address | district | neighborhood | status | newDevelopment | hasLift | parkingSpace | priceByArea | latitude | longitude | description | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| propertyCode | ||||||||||||||||||
| 101127629 | U | 176000.0 | flat | 96.0 | NaN | 3 | 2 | barrio Puerta Bonita | Carabanchel | Puerta Bonita | good | False | False | False | 1833.0 | 40.384952 | -3.732512 | Piso en Madrid, ideal para familias. La vivien... |
| 98455037 | U | 125500.0 | flat | 74.0 | NaN | 2 | 1 | barrio Puerta Bonita | Carabanchel | Puerta Bonita | good | False | True | False | 1696.0 | 40.384807 | -3.733151 | Piso en Madrid, ideal para parejas. El inmuebl... |
| 102493593 | U | 1250000.0 | chalet | 341.0 | NaN | 5 | 5 | barrio Pinar del Rey | Hortaleza | Pinar del Rey | good | False | NaN | True | 3666.0 | 40.469937 | -3.648141 | Comercializa GILMAR CONSULTING INMOBILIARIO-AR... |
| 102635141 | U | 1250000.0 | chalet | 341.0 | NaN | 5 | 5 | barrio Pinar del Rey | Hortaleza | Pinar del Rey | good | False | NaN | True | 3666.0 | 40.470146 | -3.648041 | Magnifico chalet independiente en Pinar del Re... |
# Solución: podríamos convertir los valores nulos en la categoría mayoritaria (True)
df_api["exterior"].fillna(True, inplace=True)
df_api["hasLift"].value_counts(dropna = False)
True 2134 False 602 NaN 171 Name: hasLift, dtype: int64
# Observamos si los valores que son NA se tratan de chalets
df_api.loc[df_api["hasLift"].isna()]
| floor | price | propertyType | size | exterior | rooms | bathrooms | address | district | neighborhood | status | newDevelopment | hasLift | parkingSpace | priceByArea | latitude | longitude | description | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| propertyCode | ||||||||||||||||||
| 102252223 | U | 1260000.0 | chalet | 307.0 | True | 5 | 4 | barrio Campo de las Naciones-Corralejos | Barajas | Campo de las Naciones-Corralejos | good | False | NaN | True | 4104.0 | 4.046840e+08 | -3.588403e+07 | GILMAR Consulting Inmobiliario Madrid, Conde O... |
| 101541620 | U | 1190000.0 | chalet | 240.0 | True | 4 | 3 | barrio Timón | Barajas | Timón | good | False | NaN | True | 4958.0 | 4.047147e+07 | -3.595860e+07 | QUARTIERS Expertos Inmobiliarios. Agents Immob... |
| 98857406 | U | 870000.0 | chalet | 293.0 | True | 6 | 4 | barrio Alameda de Osuna | Barajas | Alameda de Osuna | good | False | NaN | True | 2969.0 | 4.045738e+08 | -3.591681e+07 | Casa en venta muy cercana al Parque del Capric... |
| 101953745 | U | 275000.0 | chalet | 170.0 | True | 5 | 5 | Calle Trópico | Barajas | Casco Histórico de Barajas | renew | False | NaN | False | 1618.0 | 4.047533e+08 | -3.579087e+07 | VOhome propiedades Barajas vende casa en el ca... |
| 101854194 | U | 239000.0 | chalet | 81.0 | True | 4 | 1 | Calle de Gallur | Latina | Los Cármenes | good | False | NaN | False | 2951.0 | 4.039667e+08 | -3.738668e+07 | Fantástico chalet adosado a sólo 7 min de la... |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 102538200 | U | 899000.0 | chalet | 269.0 | True | 5 | 4 | barrio Salvador | San Blas | Salvador | good | False | NaN | True | 3342.0 | 4.043977e+01 | -3.633348e+00 | VOhome propiedades vende magnifico Chalet pare... |
| 101642974 | U | 899000.0 | chalet | 269.0 | True | 5 | 4 | barrio Salvador | San Blas | Salvador | good | False | NaN | True | 3342.0 | 4.043975e+01 | -3.633934e+00 | Precioso Chalet con amplio Jardin en Salvador ... |
| 102493593 | U | 1250000.0 | chalet | 341.0 | True | 5 | 5 | barrio Pinar del Rey | Hortaleza | Pinar del Rey | good | False | NaN | True | 3666.0 | 4.046994e+01 | -3.648141e+00 | Comercializa GILMAR CONSULTING INMOBILIARIO-AR... |
| 102635141 | U | 1250000.0 | chalet | 341.0 | True | 5 | 5 | barrio Pinar del Rey | Hortaleza | Pinar del Rey | good | False | NaN | True | 3666.0 | 4.047015e+01 | -3.648041e+00 | Magnifico chalet independiente en Pinar del Re... |
| 98788092 | U | 943000.0 | chalet | 425.0 | True | 3 | 4 | Calle de Estíbaliz | Hortaleza | Pinar del Rey | good | False | NaN | True | 2219.0 | 4.047044e+01 | -3.650761e+00 | Este casa / chalet se encuentra en Calle Estíb... |
171 rows × 18 columns
# Solución: podríamos convertir los valores nulos en la categoría más lógica: False
df_api["hasLift"].fillna(False, inplace=True)
# Solución: crear una categoría que englobe estos inmuebles sin descripción asignada
df_api["description"].fillna("Sin descripción", inplace=True)
df_api["neighborhood"].value_counts(dropna = False)
Vista Alegre 71
Pueblo Nuevo 66
El Viso 63
Almagro 62
Aravaca 56
..
Arroyo del Fresno 1
NaN 1
Lucero 1
Fontarrón 1
Ciudad Jardín 1
Name: neighborhood, Length: 141, dtype: int64
# Observamos el registro del inmueble cuyo barrio no ha sido especificado y vemos que se ubica en Rivas-Vaciamadrid
df_api.loc[df_api["neighborhood"].isnull()]
| floor | price | propertyType | size | exterior | rooms | bathrooms | address | district | neighborhood | status | newDevelopment | hasLift | parkingSpace | priceByArea | latitude | longitude | description | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| propertyCode | ||||||||||||||||||
| 102518604 | U | 605000.0 | chalet | 314.0 | True | 5 | 3 | Calle Emmanuel Kant | Rivas Centro | NaN | good | False | False | True | 1927.0 | 40.346588 | -3.514513 | ************ENAMORATE SIN MÁS*********** *****... |
# Solución: eliminar este registro
df_api.drop([102518604], axis=0, inplace=True)
Comprobamos que se han implementado correctamente los datos y ya no tenemos ningún valor faltante en el dataset.
df_api.isnull().sum()
floor 0 price 0 propertyType 0 size 0 exterior 0 rooms 0 bathrooms 0 address 0 district 0 neighborhood 0 status 0 newDevelopment 0 hasLift 0 parkingSpace 0 priceByArea 0 latitude 0 longitude 0 description 0 dtype: int64
En este punto, vamos a revisar las características de cada columna, centrándonos en los valores que toma cada variable y concretamente, en si existe algún valor atípico (outlier) dentro de nuestros datos.
Estos pueden tener diferentes significados: tratarse de un error en el registro de los datos, escaparse simplemente del rango donde se concentran la mayoría de valores pero ser un dato válido o puede que sea un dato que queremos detectar y que sea el objetivo del estudio. En cada caso, definiremos la estrategia que consideras más adecuada para tratarlos.
propertyCode: no conocemos si los códigos siguen un determinado patrón, pero por la naturaleza de la columna y que es el índice del dataset, no vemos necesario estudiar valores atípicos en ella.
floor: en cuanto al piso que ocupa un inmueble en el edificio, observamos que son valores numéricos entre el 1 y el 23. Además, se observan otras diferentes plantas como "bj" (bajo), "en" (entresuelo), "ss" (semi-sótano) o "st" (sótano), al igual que se observa la categoría "-1" y "U" (unifamiliar). A través del recuento y frecuencia de valores, vemos que las propiedades más frecuentes están en la altura 1, 2 y en los bajos del edificio, al igual que se tratan de viviendas unifamiliares. Por otra parte, en cuanto a los valores menos frecuentes, son las propiedades que se encuentran a mayores alturas (en el piso 19, 21 o 23). Si bien podrían ser sospechosos de considerarse atípicos, creemos que no tienen por qué, ya que las propiedades en edificios muy altos, no son muy comunes. De cualquier forma, recomendamos buscar información adicional y así poder determinar si es un dato válido o debe recodificarse.
# Detectar valores extraños en la columna floor
df_api['floor'].value_counts()
1 419 2 336 bj 332 U 320 3 296 4 260 5 148 1 115 6 113 3 103 2 95 4 73 7 58 5 37 8 29 en 26 6 22 10 20 9 18 7 14 11 11 8 8 ss 8 11 7 9 6 14 6 13 5 12 3 st 3 15 3 -1 2 14 2 10 2 12 1 21 1 17 1 23 1 19 1 13 1 Name: floor, dtype: int64
# Vemos que hay plantas que aparecen dos veces. Esto es debido a su codificación, algunas veces como tipo string
# y otras como número. Por tanto, vamos a unificarlas en formato string
df_api['floor'] = df_api['floor'].replace({"1": 1, "2":2, "3":3, "4":4, "5":5, "6":6, "7":7, "8":8, "9":9,
"10":10, "11":11, "12":12, "13":13, "14":14, "15":15, "16":16,
"17":17, "18":18, "19":19, "20":20, "21":21, "22":22, "23":23})
# Volvemos a comprobar los valores que toma la variable
df_api['floor'].value_counts()
1 534 2 431 3 399 4 333 bj 332 U 320 5 185 6 135 7 72 8 37 en 26 9 24 10 22 11 18 14 8 ss 8 13 6 12 4 15 3 st 3 -1 2 19 1 23 1 21 1 17 1 Name: floor, dtype: int64
# Detectar valores extraños en la columna price
df_api['price'].value_counts()
310000.0 24
199000.0 21
275000.0 21
189000.0 20
295000.0 19
..
144900.0 1
184000.0 1
5500000.0 1
1925000.0 1
412000.0 1
Name: price, Length: 881, dtype: int64
# Observamos las características del inmueble cuyo precio es de 8M para comprobar que no es atípico
df_api.loc[df_api["price"]==8000000.0]
| floor | price | propertyType | size | exterior | rooms | bathrooms | address | district | neighborhood | status | newDevelopment | hasLift | parkingSpace | priceByArea | latitude | longitude | description | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| propertyCode | ||||||||||||||||||
| 102343271 | 2 | 8000000.0 | flat | 473.0 | True | 4 | 5 | barrio Castellana | Barrio de Salamanca | Castellana | good | False | True | False | 16913.0 | 404294388.0 | -36865949.0 | Olisson presenta maravillosa casa de autor en ... |
col = "price"
fig = px.histogram(df_api, x=col, title=f"Histograma de la columna {col}")
# Utilizamos la media y un múltiplo de la desviación estándar para establecer los límites superior e inferior
mean = np.mean(df_api[col])
std = np.std(df_api[col])
atipicos_leves_sup = mean + 1.5*std
atipicos_leves_inf = mean - 1.5*std
fig.add_vrect(x0=atipicos_leves_inf, x1=atipicos_leves_sup, annotation_text="tipicos", annotation_position="top left",
fillcolor="green", opacity=0.25, line_width=0)
fig.show()
# Detectar valores extraños en la columna propertyType
df_api['propertyType'].value_counts()
flat 2463 chalet 156 penthouse 149 duplex 96 studio 42 Name: propertyType, dtype: int64
# Detectar valores extraños en la columna
df_api['size'].value_counts()
80.0 54
60.0 47
100.0 46
90.0 45
65.0 44
..
1111.0 1
401.0 1
272.0 1
325.0 1
425.0 1
Name: size, Length: 391, dtype: int64
col = "size"
fig = px.histogram(df_api, x=col, title=f"Histograma de la columna {col}")
# Utilizamos la media y un múltiplo de la desviación estándar para establecer los límites superior e inferior
mean = np.mean(df_api[col])
std = np.std(df_api[col])
atipicos_leves_sup = mean + 1.5*std
atipicos_leves_inf = mean - 1.5*std
fig.add_vrect(x0=atipicos_leves_inf, x1=atipicos_leves_sup, annotation_text="tipicos", annotation_position="top left",
fillcolor="green", opacity=0.25, line_width=0)
fig.show()
# Detectar valores extraños en la columna
df_api['exterior'].value_counts()
True 2628 False 278 Name: exterior, dtype: int64
# Detectar valores extraños en la columna
df_api['rooms'].value_counts()
3 1072 2 758 4 465 1 290 5 189 6 45 0 45 7 22 8 12 9 7 13 1 Name: rooms, dtype: int64
# Observamos las características del inmueble para comprobar que no es atípico
df_api.loc[df_api["rooms"]==13]
| floor | price | propertyType | size | exterior | rooms | bathrooms | address | district | neighborhood | status | newDevelopment | hasLift | parkingSpace | priceByArea | latitude | longitude | description | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| propertyCode | ||||||||||||||||||
| 102128653 | U | 2990000.0 | chalet | 1250.0 | True | 13 | 11 | barrio Aravaca | Moncloa | Aravaca | good | False | False | True | 2392.0 | 404590084.0 | -3790539.0 | Espaciosa vivienda a estrenar de 900 m² en la... |
col = "rooms"
fig = px.histogram(df_api, x=col, title=f"Histograma de la columna {col}")
# Utilizamos la media y un múltiplo de la desviación estándar para establecer los límites superior e inferior
mean = np.mean(df_api[col])
std = np.std(df_api[col])
atipicos_leves_sup = mean + 1.5*std
atipicos_leves_inf = mean - 1.5*std
fig.add_vrect(x0=atipicos_leves_inf, x1=atipicos_leves_sup, annotation_text="tipicos", annotation_position="top left",
fillcolor="green", opacity=0.25, line_width=0)
fig.show()
# Detectar valores extraños en la columna
df_api['bathrooms'].value_counts()
1 1178 2 1095 3 373 4 122 5 84 6 32 7 16 8 3 11 1 9 1 12 1 Name: bathrooms, dtype: int64
col = "bathrooms"
fig = px.histogram(df_api, x=col, title=f"Histograma de la columna {col}")
# Utilizamos la media y un múltiplo de la desviación estándar para establecer los límites superior e inferior
mean = np.mean(df_api[col])
std = np.std(df_api[col])
atipicos_leves_sup = mean + 1.5*std
atipicos_leves_inf = mean - 1.5*std
fig.add_vrect(x0=atipicos_leves_inf, x1=atipicos_leves_sup, annotation_text="tipicos", annotation_position="top left",
fillcolor="green", opacity=0.25, line_width=0)
fig.show()
# Detectar valores extraños en la columna
df_api['address'].value_counts()
barrio El Viso 33
barrio Nueva España 31
barrio Conde Orgaz-Piovera 29
barrio Almagro 26
barrio Aravaca 25
..
Puerto de Cotos 1
Calle Sierra de Cuerda Larga 1
Calle López Grass, 46 1
Calle lopez grass, 46 1
Calle Cardeñuela Riopico 1
Name: address, Length: 1553, dtype: int64
# Vemos el número de Números de calle que aparecen 1 sola vez
print(f"Hay {(df_api['address'].value_counts(dropna=False)==1).sum()} calles que aparecen 1 vez.")
Hay 1087 calles que aparecen 1 vez.
# Detectar valores extraños en la columna
df_api['district'].value_counts()
Carabanchel 262 Centro 218 Chamberà 190 Arganzuela 183 ChamartÃn 179 Ciudad Lineal 175 Fuencarral 159 Retiro 157 Barrio de Salamanca 152 Hortaleza 152 Puente de Vallecas 151 Tetuán 147 San Blas 121 Usera 111 Moncloa 111 Latina 82 Villaverde 72 Villa de Vallecas 67 Barajas 43 Moratalaz 41 Vicálvaro 33 Chamberí 33 Tetuán 25 Vicálvaro 22 Chamartín 20 Name: district, dtype: int64
Esto nos ha servido para observar que hay barrios mal codificados, por lo que aprovechamos para corregir estos errores tipográficos y así poder unificarlos.
# Corregimos los errores tipograficos
df_api['district'] = df_api['district'].replace({"Vicálvaro": "Vicálvaro", "ChamberÃ": "Chamberí",
"ChamartÃn": "Chamartín", "Tetuán": "Tetuán"})
# Volvemos a comprobar los valores que toma la variable
df_api['district'].value_counts()
Carabanchel 262 Chamberí 223 Centro 218 Chamartín 199 Arganzuela 183 Ciudad Lineal 175 Tetuán 172 Fuencarral 159 Retiro 157 Barrio de Salamanca 152 Hortaleza 152 Puente de Vallecas 151 San Blas 121 Usera 111 Moncloa 111 Latina 82 Villaverde 72 Villa de Vallecas 67 Vicálvaro 55 Barajas 43 Moratalaz 41 Name: district, dtype: int64
# Detectar valores extraños en la columna
df_api['neighborhood'].value_counts()
Vista Alegre 71
Pueblo Nuevo 66
El Viso 63
Almagro 62
Aravaca 56
..
Arcos 1
Arroyo del Fresno 1
Lucero 1
Fontarrón 1
Ciudad Jardín 1
Name: neighborhood, Length: 140, dtype: int64
# Detectar valores extraños en la columna
df_api['status'].value_counts()
good 2216 renew 551 newdevelopment 139 Name: status, dtype: int64
# Detectar valores extraños en la columna
df_api['newDevelopment'].value_counts()
False 2767 True 139 Name: newDevelopment, dtype: int64
# Detectar valores extraños en la columna
df_api['hasLift'].value_counts()
True 2134 False 772 Name: hasLift, dtype: int64
# Detectar valores extraños en la columna
df_api['parkingSpace'].value_counts()
False 1931 True 975 Name: parkingSpace, dtype: int64
# Detectar valores extraños en la columna
df_api['priceByArea'].value_counts()
5000.0 13
1853.0 12
3333.0 12
2500.0 11
4000.0 9
..
5630.0 1
4780.0 1
7268.0 1
764.0 1
4901.0 1
Name: priceByArea, Length: 2036, dtype: int64
# Vemos el número de Números de calle que aparecen 1 sola vez
print(f"Hay {(df_api['priceByArea'].value_counts(dropna=False)==1).sum()} números que aparecen 1 vez.")
Hay 1492 números que aparecen 1 vez.
col = "priceByArea"
fig = px.histogram(df_api, x=col, title=f"Histograma de la columna {col}")
# Utilizamos la media y un múltiplo de la desviación estándar para establecer los límites superior e inferior
mean = np.mean(df_api[col])
std = np.std(df_api[col])
atipicos_leves_sup = mean + 1.5*std
atipicos_leves_inf = mean - 1.5*std
fig.add_vrect(x0=atipicos_leves_inf, x1=atipicos_leves_sup, annotation_text="tipicos", annotation_position="top left",
fillcolor="green", opacity=0.25, line_width=0)
fig.show()
# Observamos las características del inmueble para comprobar que no es atípico
df_api.loc[df_api["priceByArea"]==16995.0]
| floor | price | propertyType | size | exterior | rooms | bathrooms | address | district | neighborhood | status | newDevelopment | hasLift | parkingSpace | priceByArea | latitude | longitude | description | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| propertyCode | ||||||||||||||||||
| 100736950 | 1 | 3450000.0 | flat | 203.0 | True | 4 | 5 | Calle Lagasca | Barrio de Salamanca | Recoletos | good | False | True | False | 16995.0 | 404236102.0 | -36859998.0 | Bienvenidos a una de las propiedades más excl... |
# Comprobamos los valores más frecuentes que toma la variable
df_api['longitude'].value_counts()
-3.609130e+05 10
-3.663551e+07 8
-3.702688e+07 7
-3.708643e+07 6
-3.682179e+07 6
..
-3.675186e+07 1
-3.675085e+07 1
-3.677609e+07 1
-3.676079e+07 1
-3.671040e+00 1
Name: longitude, Length: 2795, dtype: int64
Atendiendo a la estadística descriptiva, vemos que el precio medio de las propiedades supera los 650.000€, alcanzando el inmueble más caro un precio de casi 9M, mientras que el más barato se vende por 50.000€.
Los datos recogidos indican que el tamaño medio es de casi 135 m2. Por otra parte, cabe destacar que las propiedades en Madrid suelen tener 3 habitaciones y 2 baños, en mediana.
## Vemos la estadística de las variables numéricas
df_api.describe().T
| count | mean | std | min | 25% | 50% | 75% | max | |
|---|---|---|---|---|---|---|---|---|
| price | 2906.0 | 6.618963e+05 | 8.135256e+05 | 5.000000e+04 | 210000.00 | 385000.0 | 7.942500e+05 | 8.900000e+06 |
| size | 2906.0 | 1.348661e+02 | 1.206259e+02 | 1.900000e+01 | 72.00 | 100.0 | 1.500000e+02 | 1.440000e+03 |
| rooms | 2906.0 | 2.898486e+00 | 1.301800e+00 | 0.000000e+00 | 2.00 | 3.0 | 4.000000e+00 | 1.300000e+01 |
| bathrooms | 2906.0 | 1.980385e+00 | 1.179848e+00 | 1.000000e+00 | 1.00 | 2.0 | 2.000000e+00 | 1.200000e+01 |
| priceByArea | 2906.0 | 4.445637e+03 | 2.293090e+03 | 7.640000e+02 | 2769.25 | 3995.0 | 5.385000e+03 | 1.699500e+04 |
| latitude | 2906.0 | 2.820189e+08 | 1.829150e+08 | 4.035016e+01 | 40382673.25 | 403999305.5 | 4.043983e+08 | 4.051239e+08 |
| longitude | 2906.0 | -2.557606e+07 | 1.670492e+07 | -3.786427e+07 | -37027087.25 | -36667085.0 | -3.650630e+06 | -3.579571e+00 |
Por otra parte, el perfil del inmueble prototipo que encontramos es un piso en buen estado situado en el primer piso de un edificio ubicado en el distrito de Carabanchel, concretamente, en el barrio de Vista Alegre.
Es un piso exterior y no es de nueva construcción. Cuenta con ascensor, pero no con espacio de aparcamiento.
# Vemos la estadística de las variables categóricas
df_api.describe(include = (object, bool)).T
| count | unique | top | freq | |
|---|---|---|---|---|
| floor | 2906 | 25 | 1 | 534 |
| propertyType | 2906 | 5 | flat | 2463 |
| exterior | 2906 | 2 | True | 2628 |
| address | 2906 | 1553 | barrio El Viso | 33 |
| district | 2906 | 21 | Carabanchel | 262 |
| neighborhood | 2906 | 140 | Vista Alegre | 71 |
| status | 2906 | 3 | good | 2216 |
| newDevelopment | 2906 | 2 | False | 2767 |
| hasLift | 2906 | 2 | True | 2134 |
| parkingSpace | 2906 | 2 | False | 1931 |
| description | 2906 | 2800 | Gestilar presenta su nuevo proyecto de obra nu... | 10 |
# Vemos la distribución de la variable a predecir
fig = px.histogram(df_api, x="price", title=f"Distribución del precio de los inmuebles")
fig.show()
A continuación, decidimos realizar varios gráficos que nos representen a simple vista, los datos que hemos ido analizando.
# Grafico 1: proporción de inmuebles interior / exterior
sns.catplot(x="exterior", kind="count", data=df_api, height=3, aspect=2)
plt.show()
# Grafico 2: proporción de inmuebles de nueva construcción
sns.catplot(x="newDevelopment", kind="count", data=df_api, height=3, aspect=2)
plt.show()
# Grafico 3: proporción de inmuebles con ascensor
sns.catplot(x="hasLift", kind="count", data=df_api, height=3, aspect=2)
plt.show()
# Grafico 4: proporción de inmuebles según el tipo al que pertenecen
sns.catplot(x="propertyType", kind="count", data=df_api, height=3, aspect=2)
plt.show()
# Grafico 5: distritos más comunes
ax = sns.catplot(x="district", kind="count", data=df_api, height=3, aspect=2)
plt.xticks(rotation=90)
plt.show()
# Grafico 6: precio medio por distrito
plt.figure(figsize=(12,6))
barrios = df_api.groupby("district")["price"].mean()
barrios = barrios.sort_values(ascending=False)
barrios.plot.bar()
plt.show()
# Grafico 7: tipos de propiedad por distrito
barrios_types = df_api.groupby(["propertyType", "district"])["price", ].mean().reset_index()
# Agrupar los datos por distrito y "Permit Type"
grouped = barrios_types.groupby(["district", "propertyType"]).size().reset_index(name="Count")
# Contamos los tipos de propiedad únicos para cada barrio
count_types = grouped.groupby(['district'])['propertyType'].nunique().sort_values(ascending=False)
# Creamos el gráfico de barras
ax = grouped.pivot(index='district', columns='propertyType', values='Count').reindex(count_types.index).plot(kind='bar', stacked=True)
# Agregamos título y etiquetas de eje
plt.title("Tipos de propiedad por distritos")
plt.xlabel("Distrito")
plt.legend(title="Tipos de propiedad")
#ajustar tamaño
fig = ax.get_figure()
fig.set_size_inches(20, 10)
#ajustar superposiciones
plt.tight_layout()
# Mostrar el gráfico
plt.show()
Para algunos algoritmos de Machine Learning, será necesario tener las variables categóricas en formato numérico. Para ello, en este punto, vamos a utilizar diferentes técnicas para la discretización de las columnas no numéricas, para poder ser tratadas por estos algoritmos. Para ello, habiendo visto cómo se distribuyen los valores de cada una de las variables, las podemos discretizar de diferente forma.
En nuestro caso, las variables categóricas son:
df_api.dtypes
floor object price float64 propertyType object size float64 exterior bool rooms int64 bathrooms int64 address object district object neighborhood object status object newDevelopment bool hasLift bool parkingSpace bool priceByArea float64 latitude float64 longitude float64 description object dtype: object
Variables como adress o description no tienen poder predictivo, por lo que no son de interés para nuestro modelo y decidimos no tratarlas.
# Antes de comenzar, creamos una copia del df
df_clean_api = df_api.copy()
# Esto se aplica sobre las variables: exterior, newDevelopment, hasLift, parkingSpace
df_api_discretizado = df_api.replace({False: 0, True: 1})
#### Variable "floor"
# cuenta con valores que ya son numéricos, pero otros (que designan el sótano, semi-sótano, bajo, entreplanta y
# vivienda unifamiliar) son caracteres. Por ello, lo que podemos hacer es crear un diccionario únicamente para
# sustituir estos valores y así tener esta variable numérica.
# Para simplificar, los dos inmuebles en el piso -1 los consideraremos tambien sótano (corresponderán a la categoría 24)
df_api_discretizado["floor"].replace({"-1":24, "st": 24, "ss": 25, "bj": 26, "en": 27, "U": 28}, inplace=True)
#### Variable "neighborhood"
# Para convertirlo a numérico, podemos modelarlo asignando a cada valor actual, un número.
# Vemos cómo se distribuyen los valores de la variable
categorias = (df_api_discretizado[["neighborhood"]].value_counts())
# Empezamos definiendo una lista con las categorías actuales
categorias_actuales = [cat[0] for cat in categorias.index]
# Creamos las nuevas categorías, que van a corresponder con las respectivas posiciones
nuevas_categorias = [elem for elem in range(0, len(categorias_actuales))]
# Creamos el diccionario
my_mapping2 = dict()
for col_name, new_name in zip(categorias_actuales, nuevas_categorias):
my_mapping2[col_name] = new_name
my_mapping2
# Por último, sustituimos los valores de la columna por nuestro mapeo
df_api_discretizado["neighborhood"] = df_api_discretizado["neighborhood"].replace(my_mapping2)
#### Variable "district"
# Para convertirlo a numérico, podemos modelarlo asignando a cada valor actual, un número.
# Podríamos aplicar este mapeo, pero decidimos convertirlas mediante One-hot encoding (apartado a continuación)
# Vemos cómo se distribuyen los valores de la variable
categorias = (df_api_discretizado[["district"]].value_counts())
# Empezamos definiendo una lista con las categorías actuales
categorias_actuales = [cat[0] for cat in categorias.index]
# Creamos las nuevas categorías, que van a corresponder con las respectivas posiciones
nuevas_categorias = [elem for elem in range(0, len(categorias_actuales))]
# Creamos el diccionario
my_mapping = dict()
for col_name, new_name in zip(categorias_actuales, nuevas_categorias):
my_mapping[col_name] = new_name
# Vemos cuál es la asociación de valores que se ha hecho (usaremos la misma para el df de Kaggle)
print(my_mapping)
# Por último, sustituimos los valores de la columna por nuestro mapeo
df_api_discretizado["district"] = df_api_discretizado["district"].replace(my_mapping)
#### Variable "propertyType"
df_api_discretizado[["propertyType"]].value_counts(dropna=False)
# Creamos las dummies derivadas de esta variable.
y = pd.get_dummies(df_api_discretizado["propertyType"], prefix="Type")
# Añadimos al data frame original estas nuevas columnas que hemos creado
df_api_discretizado = pd.concat((df_api_discretizado, y), axis=1)
#### Variable "status"
df_api_discretizado[["status"]].value_counts(dropna=False)
# Creamos las dummies derivadas de esta variable.
y2 = pd.get_dummies(df_api_discretizado["status"], prefix="status")
# Añadimos al data frame original estas nuevas columnas que hemos creado
df_api_discretizado = pd.concat((df_api_discretizado, y2), axis=1)
Para la variable "district" podríamos aplicar un mapeo, pero decidimos aplicar One-hot encoding también, ya que no son excesivamente muchas categorías como sí ocurre con la variable "neighborhood".
#### Variable "district"
df_api_discretizado[["district"]].value_counts(dropna=False)
# Creamos las dummies derivadas de esta variable.
y3 = pd.get_dummies(df_api_discretizado["district"], prefix="district")
# Añadimos al data frame original estas nuevas columnas que hemos creado
df_api_discretizado = pd.concat((df_api_discretizado, y3), axis=1)
# De este df discretizado, eliminamos las columnas originales a las que hemos creado las dummies (excepto district, para que nos sirva en la visualizacion)
df_api_discretizado.drop(['propertyType', 'status'], axis=1, inplace=True)
# Eliminamos también las variables que no nos aportan información
# Decidimos eliminar neighborhood también, ya que nos centraremos mejor en el distrito, pues la dirección puede no estar bien codificada
df_api_discretizado.drop(['address', 'description', 'neighborhood'], axis=1, inplace=True)
Por último, mostramos cómo ha quedado el dataframe, viendo que las columnas dummies se han añadido al final:
# Mostramos el dataframe
df_api_discretizado.head()
| floor | price | size | exterior | rooms | bathrooms | district | newDevelopment | hasLift | parkingSpace | ... | district_Moncloa | district_Moratalaz | district_Puente de Vallecas | district_Retiro | district_San Blas | district_Tetuán | district_Usera | district_Vicálvaro | district_Villa de Vallecas | district_Villaverde | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| propertyCode | |||||||||||||||||||||
| 100546624 | 3 | 2300000.0 | 161.0 | 1 | 3 | 2 | Barrio de Salamanca | 0 | 1 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 102200705 | 2 | 319000.0 | 45.0 | 1 | 1 | 1 | Centro | 0 | 1 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 100688663 | 1 | 1300000.0 | 258.0 | 1 | 4 | 2 | Centro | 0 | 1 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 100762638 | 1 | 660000.0 | 183.0 | 1 | 2 | 2 | Centro | 0 | 1 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 100367054 | 4 | 695000.0 | 135.0 | 1 | 3 | 2 | Centro | 0 | 1 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
5 rows × 42 columns
# comprobamos que ya todos los tipos son numéricos
df_api_discretizado.dtypes
floor int64 price float64 size float64 exterior int64 rooms int64 bathrooms int64 district object newDevelopment int64 hasLift int64 parkingSpace int64 priceByArea float64 latitude float64 longitude float64 Type_chalet uint8 Type_duplex uint8 Type_flat uint8 Type_penthouse uint8 Type_studio uint8 status_good uint8 status_newdevelopment uint8 status_renew uint8 district_Arganzuela uint8 district_Barajas uint8 district_Barrio de Salamanca uint8 district_Carabanchel uint8 district_Centro uint8 district_Chamartín uint8 district_Chamberí uint8 district_Ciudad Lineal uint8 district_Fuencarral uint8 district_Hortaleza uint8 district_Latina uint8 district_Moncloa uint8 district_Moratalaz uint8 district_Puente de Vallecas uint8 district_Retiro uint8 district_San Blas uint8 district_Tetuán uint8 district_Usera uint8 district_Vicálvaro uint8 district_Villa de Vallecas uint8 district_Villaverde uint8 dtype: object
Dado que train (df_Kaggle) y test (df_API) deben tener el mismo esqueleto, tenemos que realizar una serie de operaciones.
Este dataset con datos extraídos de la API, va a ser el dataset de test.
df_test = df_api_discretizado.drop(['newDevelopment', 'latitude', 'longitude'], axis=1)
df_test = df_test.rename(columns={'Type_penthouse': 'Type_Áticos'})
df_test['price'] = df_test['price'].astype('int64')
df_test['priceByArea'] = df_test['priceByArea'].astype('int64')
print(df_test.shape)
df_test.head()
(2906, 39)
| floor | price | size | exterior | rooms | bathrooms | district | hasLift | parkingSpace | priceByArea | ... | district_Moncloa | district_Moratalaz | district_Puente de Vallecas | district_Retiro | district_San Blas | district_Tetuán | district_Usera | district_Vicálvaro | district_Villa de Vallecas | district_Villaverde | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| propertyCode | |||||||||||||||||||||
| 100546624 | 3 | 2300000 | 161.0 | 1 | 3 | 2 | Barrio de Salamanca | 1 | 0 | 14286 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 102200705 | 2 | 319000 | 45.0 | 1 | 1 | 1 | Centro | 1 | 0 | 7089 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 100688663 | 1 | 1300000 | 258.0 | 1 | 4 | 2 | Centro | 1 | 0 | 5039 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 100762638 | 1 | 660000 | 183.0 | 1 | 2 | 2 | Centro | 1 | 0 | 3607 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 100367054 | 4 | 695000 | 135.0 | 1 | 3 | 2 | Centro | 1 | 0 | 5148 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
5 rows × 39 columns
df_test.dtypes
floor int64 price int64 size float64 exterior int64 rooms int64 bathrooms int64 district object hasLift int64 parkingSpace int64 priceByArea int64 Type_chalet uint8 Type_duplex uint8 Type_flat uint8 Type_Áticos uint8 Type_studio uint8 status_good uint8 status_newdevelopment uint8 status_renew uint8 district_Arganzuela uint8 district_Barajas uint8 district_Barrio de Salamanca uint8 district_Carabanchel uint8 district_Centro uint8 district_Chamartín uint8 district_Chamberí uint8 district_Ciudad Lineal uint8 district_Fuencarral uint8 district_Hortaleza uint8 district_Latina uint8 district_Moncloa uint8 district_Moratalaz uint8 district_Puente de Vallecas uint8 district_Retiro uint8 district_San Blas uint8 district_Tetuán uint8 district_Usera uint8 district_Vicálvaro uint8 district_Villa de Vallecas uint8 district_Villaverde uint8 dtype: object
# Para ello, tenemos que leer los datos
df = pd.read_csv('df_train_val.csv')
# No tenemos en cuenta la columna 'id' de este dataset
df = df.drop("id", axis=1)
# Para que las columnas esten ordenadas de la misma forma:
df_test = df_test[df.columns]
df_test.to_csv("df_test.csv")
Ya estamos en disposición de entrenar modelos de ML para predecir los precios de los inmuebles.